package com.xj.util;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.Dom4JDriver;
import com.thoughtworks.xstream.io.xml.DomDriver;
import com.thoughtworks.xstream.io.xml.XmlFriendlyNameCoder;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

/**
 * @Description: XStream 工具类
 * 主要用于处理XML与javaBean之间的转化处理
 * @Author: Chuanlei.Sun
 * @Date: Created in 18/11/7 09:54
 */
@Slf4j
public final class XStreamUtils {

    /**
     * java bean 转换成格式化xml字符串
     *
     * @param t java bean
     * @return java.lang.String
     * @author Chuanlei.Sun
     * @date 18/11/11 19:51
     */
    public static <T> String beanToXml(T t) {
        if (null == t) {
            return null;
        }
        // 解决自定义别名中"_"变"__"
        XStream xStream = new XStream(new DomDriver("UTF-8", new XmlFriendlyNameCoder("-_", "_")));
        //XStream xStream = new XStream(new XppDriver(new XmlFriendlyNameCoder("_-", "_")));
        String xmlString = null;
        try {
            // 使用注解
            xStream.processAnnotations(t.getClass());
            // 自动检测注解
            xStream.autodetectAnnotations(true);
            // 去掉 class 属性
            xStream.aliasSystemAttribute(null, "class");
            xmlString = xStream.toXML(t);
        } catch (Exception e) {
            log.error(">>>>>>>>> [ERROR] java bean to xml is error:", e);
        }
        return xmlString;
    }

    /**
     * java bean 转换成压缩xml字符串
     *
     * @param t java bean
     * @return java.lang.String
     * @author Chuanlei.Sun
     * @date 2018/11/29 21:08
     */
    public static <T> String beanToCutDownXml(T t) {
        return cutDown(beanToXml(t));
    }

    /**
     * xml字符串转成java bean
     *
     * @param xmlString xml字符串
     * @param clazz     java bean类型
     * @return T
     * @author Chuanlei.Sun
     * @date 18/11/11 20:03
     */
    public static <T> T xmlToBean(String xmlString, Class<T> clazz) {
        XStream xStream = new XStream(new Dom4JDriver());
        T t = null;
        try {
            // 使用注解
            xStream.processAnnotations(clazz);
            // 自动检测注解
            xStream.autodetectAnnotations(true);
            Object object = xStream.fromXML(xmlString);
            t = clazz.cast(object);
        } catch (Exception e) {
            log.error(">>>>>>>>> [ERROR] xml to java bean is error:", e);
        }
        return t;
    }

    /**
     * xml字符串转对象
     * 用于含子对象的情况
     *
     * @param xmlString 源XML字符串
     * @param clazz     最终要解析的类
     * @param clsArray  子对象
     * @return T
     * @author Chuanlei.Sun
     * @date 2018/11/23 16:07
     */
    public static <T> T xmlToBeans(String xmlString, Class<T> clazz, Class[] clsArray) {
        T t = null;
        try {
            xmlString = addResClass(xmlString, clsArray);
            log.warn(">>>>>>>>> 替换后的XML:{}", xmlString);
            t = xmlToBean(xmlString, clazz);
        } catch (Exception e) {
            log.error(">>>>>>>>> [ERROR] xml to java bean is error:", e);
        }
        return t;
    }

    /**
     * 替换xml节点
     * 通过自定义类，和类的注解，替换注解对应节点，以达到添加属性的目的
     *
     * @param xmlString 源XML字符串
     * @param clsArray  类数组
     * @return java.lang.String
     * @author Chuanlei.Sun
     * @date 2018/11/23 16:06
     */
    private static String addResClass(String xmlString, Class[] clsArray) {
        if (StringUtils.isNotBlank(xmlString) && null != clsArray && clsArray.length > 0) {
            for (int i = 0; i < clsArray.length; i++) {
                String sourceStr = "<" + ((ReplaceEle) clsArray[i].getAnnotation(ReplaceEle.class)).value() + ">";
                String targetStr = StringUtils.substringBefore(sourceStr, ">") + " class='" + clsArray[i].getName() + "'>";
                // 明细类查询需要移除最外层的父标签
                if (i == 1) {
                    String sourceStrEnd = "</" + ((ReplaceEle) clsArray[i].getAnnotation(ReplaceEle.class)).value() + ">";
                    xmlString = StringUtils.deleteFirst(xmlString, sourceStr);
                    xmlString = StringUtils.deleteLast(xmlString, sourceStrEnd);
                }
                xmlString = xmlString.replaceAll(sourceStr, targetStr);
            }
        }
        return xmlString;
    }

    /**
     * 排除当集合为空时，标签为<ACCS/>的情况，导致平安解析不了
     * 替换<ACCS/>为<ACCS></ACCS>
     *
     * @param xmlString 源字符串
     * @param clsArray  类
     * @return java.lang.String
     * @author Chuanlei.Sun
     * @date 2018/11/26 19:44
     */
    public static String completeReqEle(String xmlString, Class[] clsArray) {
        if (StringUtils.isNotBlank(xmlString) && null != clsArray && clsArray.length > 0) {
            for (int i = 0; i < clsArray.length; i++) {
                String sourceStr = "<" + ((ReplaceEle) clsArray[i].getAnnotation(ReplaceEle.class)).value() + "/>";
                String targetStr = "<" + ((ReplaceEle) clsArray[i].getAnnotation(ReplaceEle.class)).value() + "></" + ((ReplaceEle) clsArray[i].getAnnotation(ReplaceEle.class)).value() + ">";
                xmlString = xmlString.replaceAll(sourceStr, targetStr);
            }
        }
        return xmlString;
    }

    /**
     * XML压缩
     * 移除换行符、制表符、回车键、标签间的空格符
     *
     * @param xml 原始XML
     * @return java.lang.String
     * @author Chuanlei.Sun
     * @date 2018/11/29 20:06
     */
    public static String cutDown(String xml) {
        if (StringUtils.isNotBlank(xml)) {
            return xml.replaceAll("\n", "").replaceAll("\r", "").replaceAll("\t", "").replaceAll(">\\s*<", "><");
        }
        return xml;
    }

	/**
	 * xml文件转实体
	 * @param filePath
	 * @param clazz
	 * @param <T>
	 * @return
	 */
	public static <T> T xmlFileToBean(String filePath, Class<T> clazz)  {
		File file = new File(filePath);
		XStream xStream = new XStream(new Dom4JDriver());
		T t = null;
		try {
			InputStream input = new FileInputStream(file);
			xStream.allowTypeHierarchy(clazz);
			// 使用注解
			xStream.processAnnotations(clazz);
			// 自动检测注解
			xStream.autodetectAnnotations(true);
			Object object = xStream.fromXML(input);
			t = clazz.cast(object);
		} catch (Exception e) {
			log.error(">>>>>>>>> [ERROR] xml to java bean is error:", e);
		}
		return t;
	}


}
